package com.demo.util;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class PasswordEncryption {

    // 定义加密算法和模式
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
    // 定义初始化向量，必须是16个字节
    private static final byte[] IV = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04,
            0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
            0x0F };

    // 定义一个方法，接受一个字符串作为参数，返回其加密后的字符串
    public static String encryptPassword(String password, byte[] salt) {
        try {
            // 创建一个KeySpec对象，指定密码和盐值
            KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536,
                    256);
            // 创建一个SecretKeyFactory对象，指定使用PBKDF2WithHmacSHA256算法
            SecretKeyFactory factory = SecretKeyFactory
                    .getInstance("PBKDF2WithHmacSHA256");
            // 生成一个密钥
            SecretKey secretKey = factory.generateSecret(spec);
            // 将密钥转换为字节数组
            byte[] key = secretKey.getEncoded();
            // 创建一个SecretKeySpec对象，指定使用AES算法
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            // 创建一个Cipher对象，指定使用AES/CBC/PKCS5Padding模式
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            // 初始化Cipher对象，设置为加密模式，并传入密钥和初始化向量
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,
                    new IvParameterSpec(IV));
            // 对密码字节数组进行加密，并返回字节数组
            byte[] encryptedBytes = cipher.doFinal(password.getBytes());
            // 将字节数组转换为十六进制字符串，并返回
            return byteArrayToHexString(encryptedBytes);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException
                 | javax.crypto.NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (java.security.InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (java.security.InvalidKeyException e) {
            e.printStackTrace();
        } catch (javax.crypto.BadPaddingException e) {
            e.printStackTrace();
        } catch (javax.crypto.IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 定义一个方法，接受一个字符串作为参数，返回其是否与数据库中的加密后的密码匹配
    public static boolean validatePassword(String password,
                                           String encryptedPassword, byte[] salt) {
        // 调用encryptPassword方法，对输入的密码进行加密
        String newEncryptedPassword = encryptPassword(password, salt);
        // 比较加密后的密码和数据库中的加密后的密码是否相同，并返回结果
        return newEncryptedPassword.equals(encryptedPassword);
    }

    // 定义一个方法，将字节数组转换为十六进制字符串
    private static String byteArrayToHexString(byte[] b) {
        StringBuffer sb = new StringBuffer(b.length * 2);
        for (int i = 0; i < b.length; i++) {
            int v = b[i] & 0xff;
            if (v < 16) {
                sb.append('0');
            }
            sb.append(Integer.toHexString(v));
        }
        return sb.toString().toUpperCase();
    }

    // 测试方法，输入一个密码，输出其加密后的字符串和验证结果
    public static void main(String[] args) {
        // 定义一个密码
        String password = "ccc";
        // 定义一个盐值，可以是随机生成的
        byte[] salt = new byte[] { 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05,
                0x04, 0x03, 0x02, 0x01, 0x00 };
        // 调用encryptPassword方法，对密码进行加密
        String encryptedPassword = encryptPassword(password, salt);
        // 输出原密码和加密后的密码
        System.out.println("原密码：" + password);
        System.out.println("加密后的密码：" + encryptedPassword);
        // 调用validatePassword方法，验证密码是否正确
        boolean result = validatePassword(password, encryptedPassword, salt);

        // 输出验证结果
        System.out.println("验证结果：" + result);
    }
}
